﻿using CSRedis;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;

namespace Cache
{

    /// <summary>
    /// 内存缓存
    /// </summary>
    public class RedisCache : ICache
    {
        /// <summary>
        /// 缓存配置
        /// </summary>
        public CacheConfig CacheConfig { get; set; }

        /// <summary>
        /// 当前缓存的集合
        /// </summary>
        public List<object> KeyList { get; set; }

        /// <summary>
        /// Redis连接对象
        /// 该对象可在所有调用间共享，且线程安全，不需要重复创建
        /// </summary>
        public CSRedisClient[] dbs = new CSRedisClient[15];

        /// <summary>
        /// Redis连接对象
        /// 该对象可在所有调用间共享，且线程安全，不需要重复创建
        /// </summary>
        private CSRedisClient db;

        /// <summary>
        /// 构造函数
        /// </summary>
        public RedisCache()
        {

        }

        /// <summary>
        /// 初始化redis
        /// </summary>
        public void Init()
        {
            // "127.0.0.1:6379,allowadmin=true";
            // "127.0.0.1:6379,password=123,poolsize=10,ssl=false,writeBuffer=10240,prefix=key前辍"
            db = new CSRedisClient(string.Format("{0},defaultDatabase={1},poolsize=10", CacheConfig.ConnectionString, CacheConfig.Index));
            for (var i = 0; i < dbs.Length; i++) dbs[i] = new CSRedisClient(CacheConfig.ConnectionString + "; defualtDatabase=" + i);
            KeyList = new List<object>();
        }

        /// <summary>
        /// 设置泛型缓存项，如该项已存在则覆盖原有值（自动序列化为JSON格式）
        /// </summary>
        /// <param name="key">缓存key值</param>
        /// <param name="value">缓存value值</param>
        /// <param name="expirationTime">过期时间</param>
        /// <returns>对象</returns>
        public void Set(object key, object value, int? expirationTime)
        {
            var prefix = string.IsNullOrWhiteSpace(CacheConfig.Prefix) ? "" : CacheConfig.Prefix + ":";
            //IDatabase db = redis.GetDatabase(GlobalData.CacheConfig.Index);
            var result = db.Set(prefix + key, value, expirationTime.HasValue ? expirationTime.Value : (int)EnumCacheTime.Global);
            if (!KeyList.Contains(prefix + key))
                KeyList.Add(prefix + key);
        }

        /// <summary>
        /// 获取泛型缓存项
        /// </summary>
        /// <param name="key">缓存key值</param>
        /// <returns>返回缓存泛型实例对象，不存在的key则返回null</returns>
        public string Get(object key, int? expirationTime = null)
        {
            var obj = string.Empty;
            try
            {
                var redisValue = string.Empty;
                var prefix = string.IsNullOrWhiteSpace(CacheConfig.Prefix) ? "" : CacheConfig.Prefix + ":";
                redisValue = db.Get(prefix + key);
                if (string.IsNullOrWhiteSpace(redisValue))
                {
                    obj = "";
                }
                else
                    obj = redisValue;
            }
            catch (Exception)
            { }
            return obj;
        }
        public string GetValue(string key)
        {
            if (db.Exists(key))
                return db.Get(key);
            return "";
        }

        /// <summary>
        /// 获取泛型缓存项
        /// </summary>
        /// <param name="key">缓存key值</param>
        /// <returns>返回缓存泛型实例对象，不存在的key则返回null</returns>
        public T Get<T>(object key, Func<T> func = null, int? expirationTime = null)
        {
            var obj = default(T);
            try
            {
                var redisValue = string.Empty;
                var prefix = string.IsNullOrWhiteSpace(CacheConfig.Prefix) ? "" : CacheConfig.Prefix + ":";
                //if (KeyList.Contains(prefix + key))
                //{
                redisValue = db.Get(prefix + key);
                //}
                if (string.IsNullOrWhiteSpace(redisValue))
                {
                    if (func != null)
                    {
                        obj = func();
                        if (obj != null)
                            Set(key, obj, expirationTime ?? (int)EnumCacheTime.Global);
                    }
                }
                else
                    obj = JsonConvert.DeserializeObject<T>(redisValue);
            }
            catch (Exception)
            { }
            return obj;
        }

        /// <summary>
        /// 获得全部key
        /// </summary>
        /// <returns></returns>
        public List<string> Keys()
        {
            return db.Keys("*").ToList();
        }

        /// <summary>
        /// 移除指定缓存
        /// </summary>
        /// <param name="key">缓存key值</param>
        public void Remove(object key)
        {
            var prefix = string.IsNullOrWhiteSpace(CacheConfig.Prefix) ? "" : CacheConfig.Prefix + ":";
            if (KeyList.Contains(prefix + key))
                KeyList.Remove(prefix + key);
            db.Del(prefix + key);
        }

        /// <summary>
        /// 移除所有缓存
        /// </summary>
        public void RemoveAll()
        {
            List<string> keys = db.Keys("*").ToList();
            for (int i = 0; i < keys.Count; i++)
            {
                Remove(keys[i]);
            }
            KeyList = new List<object>();
        }

        /// <summary>
        /// 获取泛型缓存项
        /// </summary>
        /// <param name="key">缓存key值</param>
        /// <param name="func">执行方法</param>
        /// <param name="expirationTime">过期时间</param>
        /// <returns>返回缓存泛型实例对象，不存在的key则返回null</returns>
        public dynamic Get(object key, Func<object> func, int? expirationTime)
        {
            var prefix = string.IsNullOrWhiteSpace(CacheConfig.Prefix) ? "" : CacheConfig.Prefix + ":";
            var obj = db.Get<dynamic>(prefix + key);
            if (obj == null)
            {
                if (func != null)
                {
                    obj = func();
                    if (obj != null)
                        Set(key, obj, expirationTime ?? (int)EnumCacheTime.Global);
                }
            }
            return obj;
        }

        /// <summary>
        /// 将一个或多个值插入到列表头部
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="key">缓存key</param>
        /// <param name="obj">成员</param>
        /// <returns>插入的个数</returns>
        public long LPush<T>(string key, T obj)
        {
            return db.LPush<T>(key, obj);
        }

        /// <summary>
        /// 移除并获取列表的第一个元素
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="key">缓存key</param>
        /// <returns>列表的第一个元素</returns>
        public T LPop<T>(string key)
        {
            return db.LPop<T>(key);
        }

        /// <summary>
        /// 在列表中添加一个或多个值
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="key">缓存key</param>
        /// <param name="obj">成员</param>
        /// <returns>添加的个数</returns>
        public long RPush<T>(string key, T obj)
        {
            return db.RPush<T>(key, obj);
        }

        /// <summary>
        /// 移除并获取列表的最后一个元素
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="key">缓存key</param>
        /// <returns>列表的最后一个元素</returns>
        public T RPop<T>(string key)
        {
            return db.RPop<T>(key);
        }

        /// <summary>
        /// 获取指定返回内的集合
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="key">缓存key</param>
        /// <param name="start">开始索引</param>
        /// <param name="stop">结束索引</param>
        /// <returns>指定返回内的集合</returns>
        public T[] LRange<T>(string key, int start, int stop)
        {
            return db.LRange<T>(key, start, stop);
        }

        /// <summary>
        /// 向集合中添加一个或多个成员
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <param name="obj">成员</param>
        /// <returns>添加个数</returns>
        public long SAdd<T>(string key, T obj)
        {
            return db.SAdd<T>(key, obj);
        }

        /// <summary>
        /// 返回成员集合
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <returns>成员集合</returns>
        public T[] SMembers<T>(string key)
        {
            return db.SMembers<T>(key);
        }
        /// <summary>
        /// 返回成员集合
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <returns>成员集合</returns>
        public string[] SMembers(string key)
        {
            return db.SMembers(key);
        }
        /// <summary>
        /// 移除集合中的一个或多个成员
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="key">缓存key</param>
        /// <param name="members">成员集合</param>
        /// <returns>移除的个数</returns>
        public long SRem<T>(string key, T[] members)
        {
            return db.SRem<T>(key, members);
        }
        /// <summary>
        /// 移除集合中的一个或多个成员
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="key">缓存key</param>
        /// <param name="members">成员集合</param>
        /// <returns>移除的个数</returns>
        public long SRem(string key, string[] members)
        {
            return db.SRem(key, members);
        }
        /// <summary>
        /// 是否存在缓存key
        /// </summary>
        /// <param name="key">缓存key</param>
        /// <returns>是否存在</returns>
        public bool Exists(string key)
        {
            return db.Exists(key);
        }
        /// <summary>
        /// 分布式锁
        /// </summary>
        /// <param name="key">锁key</param>
        /// <param name="lockExpirySeconds">锁自动超时时间(秒)</param>
        /// <param name="waitLockMs">等待锁时间(秒)</param>
        /// <returns></returns>
        public bool Lock(string key, int lockExpirySeconds = 10, double waitLockSeconds = 0)
        {
            string lockKey = "LockForSetNX:" + key;
            bool result = db.SetNx(lockKey, key);
            if (result)
                db.Expire(lockKey, lockExpirySeconds);
            return result;
        }
        /// <summary>
        /// 删除锁 执行完代码以后调用释放锁
        /// </summary>
        /// <param name="key"></param>
        public bool DelLock(string key)
        {
            string lockKey = "LockForSetNX:" + key;
            return db.Del(lockKey) > 0;
        }
    }
}
